/**
 * 一、 
    1、var 声明的变量会变量提升，在 for 循环中 var 声明的变量 i 也会有一个变量提升，提升到全局，所以全局就会有一个变量 i，每循环一次，就会重新赋值一次，
       直到最后一次 i = 10；条件不成立，终止循环， 所以 i 最终为10；也就是说全局的 i 为10；
    2、变量 a 是一个数组，数组是一个引用类型，所以变量 a 其实是赋值了一个二进制的堆地址，这个地址指向的是一个堆空间。 
    3、for 循环中 a[i] = function () { console.log(i) }; 在数组中存储了 9 个 function， 函数内容为 console.log(i)，
    4、a[6](); 执行数组 a 中下标为 6 的函数；它就会在自己的作用域中查找 i，发现没有就向上查找，数组所在的空间也没有 i，继续向上查找全局中有一个 i， 
       i 为 10， 那么 a[6] 执行 console.log(i) 时， i = 10; 
    5、所以最后的结果是10；

二、
    1、全局会有一个变量 tmp 值为 123；
    2、if 语句会有自己的块级作用域， if true 是符合条件的， 会向下执行到 console.log(tmp), 首先会在 if 自己的作用域中找有没有 tmp，发现有一个，是let
       声明的， let 声明的变量不会变脸提升，但是 if 块及作用域中确实有 tmp 这个变量， 它就不会向上查找， 但是 console.log(tmp) 在 let tmp 之前，let
       又不会变量提升。
    3、所以会报错。 不能再变量 tmp 声明之前访问 tmp。

三、
    1、Math.min(...arr);

四、 
    1、var 声明的变量，可以重复声明，可重复赋值，没有块的概念，可以跨块访问，但是不能跨函数访问；
    2、let 声明的变量，有自己独立的快作用域，不可重复声明，可重复赋值；
    3、const 声明的是常量，有自己独立的快作用域，不可重复声明、不可重新赋值；

五、
    1、var 声明变量 a 值为 10；
    2、var 声明 obj 是一个对象 它的值为一个二进制的对地址。对象中有一个 a 值为20；有一个函数 fn 它的值为一个二进制的对地址，函数体为
       setTimout(() => {console.log(this.a)});
    3、obj.fn()执行， 去 obj 对应的对地址中找到 fn 对应的对地址，执行宏任务setTimout， setTimout中的第一个参数是箭头函数，this 指向它的上下文，
       它的上级 fn 指向 obj，那么这个箭头函数也指向 obj， 当 setTimout 执行完之后被 event loop 监听到以后，开始执行console.log(this.a)，this 指向
       obj，所以从 obj 中找 a， obj 中有 a，值为 20。
    4、 结果为 20；

六、
    1、Symbol 最主要的作用是为对象添加独一无二的属性名；
    2、如果在全局中复用一个相同的 Symbol 值，可以使用 Symbol提供的一个静态方法 for， lg：const s = Symbol.for('a')；
    3、Symbol 可以给对象设置私有属性。 lg：const obj = { [Symbol()]: 's_val'}，想要获取可以通过 Object.getOwnPropertySymbols(obj) 来获取；

七、 
    1、对象是引用类型，它指向一个堆地址。 
    2、浅拷贝
        a、浅拷贝：简单的说就是拷贝了引用，但是没有拷贝具体的值。对象指向一个堆地址，浅拷贝就是复制了一个地址，拷贝的对象和原对象都指向同一个堆地址。
        b、举个例子，有一个 a 对象，b 对象浅拷贝了 a 对象，那么 b 对象修改其内部的值的时候，对象 a 内部的值也会更改，因为他们俩指向的是同一个地址，
            也可以说他们俩引用的是同一个堆地址。
        c、实现方式：
            1、可以直用 = 赋值；
            2、可以用 for in 循环对对象内部的值一一赋复制；
            3、Object.assign；
            4、jQuery中的extend，它的第一个参数 true 为深拷贝，false 为浅拷贝，默认为false；
    3、深拷贝
        a、深拷贝：就是把其内部的值一一复制，开辟了另一个堆空间，指向一个新的堆地址，把值存储进去。
        b、同样的例子，有一个 a 对象，b 对象浅拷贝了 a 对象，那么 b 对象修改其内部的值的时候，对象 a 内部的值是不会更改的，因为他们俩指向不同的地址，
            也可以说他们俩引用了不同的地址，互不干扰。
        c、实现方式：
            1、使用 JSON 对象来实现（ JSON.parse(JSON.stringify()) ）；
            2、jQuery 中的 extend，它的第一个参数 true 为深拷贝，false 为浅拷贝，默认为 false，它会把__proto__上的属性也拷贝下来，直接拷贝到新对象上；
            3、lodash 中的 cloneDeep();
            4、通过递归判断，把对象的每一个值赋值给另一个对象

八、
    1、TypeScript 是 JavaScript 的超集，是一种基于 JavaScript 的强类型语言。
    2、TypeScript 可以运行 JavaScript 代码。
    3、TypeScript 可以编译成 JavaScript。

九、
    优点：
    1、类型规定比较明确（前提不是 any TypeScript），上减少了后期维护成本；
    2、TypeScript 的接口、泛型、类在实际项目开发中运用较多。举个例子，在后端返回的接口字段中，用 ts 定义几口字段，假如对某个字段进行了更改，或者只是更改了
        类型，ts 就会检测到，报错，我们能确切的知道哪里要做相对应的更改，而不用去找对应的逻辑，大大减少了维护成本。
    缺点：（在长期使用ts后，其实个人觉得缺点其实可以忽略，任何技术都有其学习成本，但是 ts 的优点，在编码上和维护上确实特别棒）
    1、增加了学习成本，刚开始编写 TypeScript 代码没有之前编写 JavaScript 代码顺畅。

十、
    1、引用计数工作原理
        内部有一个引用器，当引用关系改变时修改引用数字，引用数字为 0 时，立即回收。
    2、优点
        1、发现垃圾时立即回收（也就是当前对象的引用数为 0 时，立即回收）；
        2、最大限度的减少程序的暂停（当内存空间快要满的时候，去找到那些引用数为 0 的对象，立即释放）；
    3、缺点
        1、无法回收循环引用对象。
        2、事件开销大（引用计数算法内部会维护一个值来计算监听对象的引用数值是否发生改变，从监听到修改会消耗时间）。

十一、
    1、标记整理算法的工作流程
        1、标记整理可以看做是标记清除的增强，常用于老生代垃圾回收。
        2、标记阶段 的操作和标记清除一致，都是先遍历所有的对象找到并标记活动对象
        3、在清除阶段 标记整理算法在清除阶段会先执行整理，移动对象位置，能够在地址上产生连续，减少碎片化空间，然后进行清除；
           而标记清除会遍历所有对象，清除没有标记的对象。

十二、v8 中新生代存储区垃圾回收流程
    1、回收过程采用复制算法 + 标记整理算法
    2、新生代内存区分为两个等大小的空间（TO，From）
    3、使用空间为 From，空闲空间为 To
    4、活动对象存储于 From
    5、当 From 空间应用到一定程度后，就会触发 GC 操作，标记整理后将活动对象拷贝至 To
    6、From 与 To 交换空间，完场释放（释放From）

十三、
    1、标记增量是巍峨解决标记清除的长停顿问题，因为标记清除就算发现没有标记的对象也不会立即回收，等程序执行完了之后一块进行清除。
    2、标记增量工作原理 是将当前一整段的垃圾回收操作拆成多个小部分组合着完成当前的整个回收，代替之前程序执行完后一块做垃圾回收操作。
    3、标记增量的好处是 实现了垃圾回收和程序执行交替着完成，不会出现长停顿的问题也就是说不会再垃圾回收时不能做程序执行，程序执行时不能做垃圾回收
 */
